#include "stdafx.h"
#include "ui_builder.h"
#include "ui_xml_rapid_document.h"
#include "ui_export.h"


_SSL_BEGIN
_SSUIL_BEGIN

UIBuilder::UIBuilder()
{

}

UIBuilder::~UIBuilder()
{

}

bool UIBuilder::AppendBuildCallback(
	IUIBuilderCallbackInterface *cb
	)
{
	if(cb == NULL)
		return false;

	IUIBuilderCallbackInterfaceAutoPtrs::iterator iter = std::find(build_callbacks_.begin(), build_callbacks_.end(), cb);
	if(iter != build_callbacks_.end())
		return false;

	build_callbacks_.push_back(cb);
	return true;
}

bool UIBuilder::RemoveBuildCallback(IUIBuilderCallbackInterface *cb)
{
	if(cb == NULL)
		return false;

	IUIBuilderCallbackInterfaceAutoPtrs::iterator iter = std::find(build_callbacks_.begin(), build_callbacks_.end(), cb);
	if(iter == build_callbacks_.end())
		return false;

	build_callbacks_.erase(iter);
	return true;
}

void UIBuilder::ClearBuildCallback()
{
	build_callbacks_.clear();
}

bool UIBuilder::Build(
	ss_in_ IUIDiskFilesManagerInterface *disk_man,
	ss_in_ const tchar* ui_xml_file,
	ss_in_ const tchar* xml_window_node_name,
	ss_in_ bool load_global_resource,
	ss_in_ IUIWindowBgCanvasInterface *bg_canvas_,
	ss_in_ IUIResourceRenderInterface *res_ren,
	ss_out_ scoped_ref_ptr<IUIWindow> &ui_win,
	ss_in_ HWND parent_win_h /*= NULL*/,
	ss_in_ UINT id /*= 0*/,
	ss_in_ HINSTANCE inst /*= NULL*/
	)
{
	if(disk_man == NULL || ui_win != NULL)
		return false;

	disk_files_manager_ = disk_man;

	scoped_ref_ptr<IUIXMLDocumentInterface> xml_document;
	if(!LoadXml(ui_xml_file, xml_document) || xml_document == NULL)
		return false;

	return BuildWindow(xml_document, xml_window_node_name,
		load_global_resource, bg_canvas_, res_ren, ui_win,
		parent_win_h, id, inst);
}

bool UIBuilder::BuildControl(
	ss_in_ IUIXMLDocumentInterface *xml_document,
	ss_in_ UIXmlNode node,
	ss_out_ scoped_ref_ptr<IUIControlInterface> &control
	)
{
	control = NULL;

	if(xml_document == NULL || node == NULL)
		return false;

	auto_cvt class_name(xml_document->NodeName(node));
	const tchar *name = class_name.c_tstr();
	if(name == NULL || _tcslen(name) == 0)
		return false;

	if(_tcsicmp(name, _T("Control")) == 0)
	{
		control.assign_no_ref(new UIControl());
	}else if(_tcsicmp(name, _T("Image")) == 0)
	{
		control.assign_no_ref(new UIImage);
	}else if(_tcsicmp(name, _T("ImageViewer")) == 0)
	{
		control.assign_no_ref(new UIImageViewer());
	}else if(_tcsicmp(name, _T("Label")) == 0)
	{
		control.assign_no_ref(new UILabel());
	}else if(_tcsicmp(name, _T("FocusableControl")) == 0)
	{
		control.assign_no_ref(new UIFocusableControl());
	}else if(_tcsicmp(name, _T("Layout")) == 0)
	{
		control.assign_no_ref(new UILayout());
	}else if(_tcsicmp(name, _T("HorizontalLayout")) == 0)
	{
		control.assign_no_ref(new UIHorizontalLayout());
	}else if(_tcsicmp(name, _T("VerticalLayout")) == 0)
	{
		control.assign_no_ref(new UIVerticalLayout());
	}else if(_tcsicmp(name, _T("Button")) == 0)
	{
		control.assign_no_ref(new UIButton());
	}else if(_tcsicmp(name, _T("Title")) == 0)
	{
		control.assign_no_ref(new UITitle());
	}else if(_tcsicmp(name, _T("FlowLayout")) == 0)
	{
		control.assign_no_ref(new UIFlowLayout());
	}else if(_tcsicmp(name, _T("Slider")) == 0)
	{
		control.assign_no_ref(new UISlider());
	}else if(_tcsicmp(name, _T("SingleObjectControl")) == 0)
	{
		control.assign_no_ref(new UISingleObjectControl());
	}else if(_tcsicmp(name, _T("SingleObjectViewer")) == 0)
	{
		control.assign_no_ref(new UISingleObjectViewer());
	}else
	{
		for(IUIBuilderCallbackInterfaceAutoPtrs::iterator iter=build_callbacks_.begin();
			iter!=build_callbacks_.end() && control == NULL; iter++)
		{
			if(!(*iter)->CreateControl(class_name.c_tstr(), control))
			{
				control = NULL;
			}
		}
	}

	if(control == NULL)
		return false;

	if(!control->BuildControlFromXML(this, xml_document, node))
	{
		control = NULL;
	}
	return control != NULL;
}

bool UIBuilder::BuildControl(
	ss_in_ const tstring &ui_description,
	ss_out_ scoped_ref_ptr<IUIControlInterface> &control
	)
{
	tstring xml_content;
	if(ui_description.find(_T("<?xml")) == tstring::npos)
	{
		xml_content = _T("<?xml version=\"1.0\" encoding=\"UTF-8\"?>");
		xml_content += ui_description;
	}else
	{
		xml_content = ui_description;
	}

	scoped_ref_ptr<IUIXMLDocumentInterface> xml_doc;
	xml_doc.assign_no_ref(new UIXmlRapidDocument);
	if(!xml_doc->Parse(auto_cvt(xml_content.c_str()).c_utf8str()))
	{
		xml_doc = NULL;
		return false;
	}

	return BuildControl(xml_doc, xml_doc->FirstNode(NULL), control);
}

bool UIBuilder::LoadXml(
	ss_in_ const tchar *xml_file,
	ss_out_ scoped_ref_ptr<IUIXMLDocumentInterface> &xml_doc
	)
{
	xml_doc = NULL;

	if(disk_files_manager_ == NULL)
		return false;

	ssbin xml_buffer;
	if(disk_files_manager_->GetFileBuffer(xml_file, xml_buffer, 2) == NULL)
		return false;

	xml_doc.assign_no_ref(new UIXmlRapidDocument);
	if(!xml_doc->Parse(auto_cvt((const char*)xml_buffer.ptr()).c_utf8str()))
	{
		xml_doc = NULL;
		return false;
	}

	return true;
}

bool UIBuilder::BuildWindow(
	ss_in_ IUIXMLDocumentInterface *xml_document,
	ss_in_ const tchar* xml_window_node_name,
	ss_in_ bool load_global_resource,
	ss_in_ IUIWindowBgCanvasInterface *bg_canvas,
	ss_in_ IUIResourceRenderInterface *res_ren,
	ss_out_ scoped_ref_ptr<IUIWindow> &ui_win,
	ss_in_ HWND parent_win_h,
	ss_in_ UINT id,
	ss_in_ HINSTANCE inst
	)
{
	if(disk_files_manager_ == NULL)
		return false;

	if(xml_document == NULL || ui_win != NULL)
		return false;

	scoped_ref_ptr<IUIWindow> ui_win_l;
	scoped_ref_ptr<IUIResourceRenderInterface> res_ren_l = res_ren;

	for(IUIBuilderCallbackInterfaceAutoPtrs::iterator iter=build_callbacks_.begin();
		iter!=build_callbacks_.end() && res_ren_l == NULL; iter++)
	{
		res_ren_l = (*iter)->CreateResourceRender(res_ren_l);
	}

	if(res_ren_l == NULL)
		return false;

	scoped_ref_ptr<IUIControlInterface> root_control;

	UIXmlNode node = NULL;

	if(load_global_resource)
	{
		node = xml_document->FirstNode("GlobalResource");
		if(node != NULL)
		{
			BuildGlobalRes(xml_document, node, res_ren_l);
		}
	}

	bool ret = false;
	UIXmlNode child_node = NULL;

	node = xml_document->FirstNode("Window");
	if(node == NULL)
	{
		goto finish;
	}

	if(xml_window_node_name != NULL && _tcslen(xml_window_node_name) > 0)
	{
		auto_cvt win_node_name(xml_window_node_name);
		do 
		{
			UIXmlAttribute attribute = xml_document->FirstAttribute(node, "name");
			if(strcmp(xml_document->AttributeValue(attribute), win_node_name.c_astr()) == 0)
				break;
		} while((node = xml_document->NextSiblingNode(node, "Window")) != NULL);
	}

	if(node == NULL)
	{
		goto finish;
	}

	child_node = xml_document->FirstNode(node, NULL);
	if(xml_document->NextSiblingNode(child_node, NULL) != NULL)
	{
		goto finish;
	}

	if(!BuildControl(xml_document, child_node, root_control) || root_control == NULL)
	{
		goto finish;
	}

	ret = BuildUIWindow(xml_document, node, bg_canvas, res_ren_l, disk_files_manager_, ui_win_l,
		parent_win_h, id, inst);
	if(!ret || ui_win_l == NULL)
	{
		goto finish;
	}
	ui_win_l->GetWindowManager()->SetContent(root_control);

	ret = true;
finish:
	if(!ret)
	{
		root_control = NULL;
	}else
	{
		ui_win = ui_win_l;
	}
	return root_control != NULL;
}

bool UIBuilder::BuildGlobalRes(
	ss_in_ IUIXMLDocumentInterface *xml_document,
	ss_in_ UIXmlNode node,
	ss_inout_ IUIResourceRenderInterface *res_ren)
{
	if(xml_document == NULL || node == NULL)
		return false;

	if(res_ren == NULL)
		return false;

	if(strcmp(xml_document->NodeName(node), "GlobalResource") != 0)
		return false;

	for(UIXmlNode child_node = xml_document->FirstNode(node, NULL); child_node!=NULL;
		child_node = xml_document->NextSiblingNode(child_node, NULL))
	{
		if(strcmp(xml_document->NodeName(child_node), "Font") == 0)
		{
			tstring font_id, font_name;
			int size = 0;
			bool bold = false, underline = false, italic = false, def_font = false;

			UIXmlAttribute attribute = xml_document->FirstAttribute(child_node, "name");
			if(attribute == NULL)
			{
				TraceVW(_T("Font node no name attribute"));
				continue;
			}
			font_name = auto_cvt(xml_document->AttributeValue(attribute)).c_tstr();
			if(font_name.empty())
			{
				TraceVW(_T("Font node name is empty"));
				continue;
			}

			attribute = xml_document->FirstAttribute(child_node, "size");
			if(attribute == NULL)
			{
				TraceVW(_T("Font node no size attribute"));
				continue;
			}
			size = strtoul(xml_document->AttributeValue(attribute), NULL, 0);
			if(size == 0 || size > 5000)
			{
				TraceVW(_T("Font node no size attribute is %d, skip it"), size);
				continue;
			}

			attribute = xml_document->FirstAttribute(child_node, "default");
			if(attribute != NULL)
			{
				UIValueParseUtil::ParseBool(xml_document->AttributeValue(attribute), def_font);
			}

			attribute = xml_document->FirstAttribute(child_node, "id");
			if(attribute != NULL)
			{
				font_id = auto_cvt(xml_document->AttributeValue(attribute)).c_tstr();
			}

			if(font_id.empty() && !def_font)
			{
				TraceVW(_T("Font no id"));
				continue;
			}

			attribute = xml_document->FirstAttribute(child_node, "bold");
			if(attribute != NULL)
			{
				UIValueParseUtil::ParseBool(xml_document->AttributeValue(attribute), bold);
			}
			attribute = xml_document->FirstAttribute(child_node, "underline");
			if(attribute != NULL)
			{
				UIValueParseUtil::ParseBool(xml_document->AttributeValue(attribute), underline);
			}

			attribute = xml_document->FirstAttribute(child_node, "italic");
			if(attribute != NULL)
			{
				UIValueParseUtil::ParseBool(xml_document->AttributeValue(attribute), italic);
			}

			if(def_font)
			{
				SS_ASSERT(!font_id.empty());
				res_ren->SetDefaultFont(font_id.c_str());
			}

			if(!font_id.empty())
			{
				res_ren->RecordFont(font_id.c_str(), font_name.c_str(), size, bold, underline, italic);
			}
		}else if(strcmp(xml_document->NodeName(child_node), "name_value_pair") == 0)
		{
			UIXmlAttribute attribute = xml_document->FirstAttribute(child_node, "name");
			if(attribute == NULL)
			{
				TraceVW(_T("Font name_value_pair node no name attribute"));
				continue;
			}

			UIXmlAttribute attribute_type = xml_document->FirstAttribute(child_node, "type");
			if(attribute_type == NULL)
			{
				TraceVW(_T("Font name_value_pair node no type attribute"));
				continue;
			}

			UIXmlAttribute attribute_value = xml_document->FirstAttribute(child_node, "value");
			if(attribute_value == NULL)
			{
				TraceVW(_T("Font name_value_pair node no value attribute"));
				continue;
			}

			res_ren->RecordNameValuePair(
				auto_cvt(xml_document->AttributeValue(attribute)).c_tstr(),
				auto_cvt(xml_document->AttributeValue(attribute_type)).c_tstr(),
				auto_cvt(xml_document->AttributeValue(attribute_value)).c_tstr());
		}
	}
	return true;
}

bool UIBuilder::BuildUIWindow(
	ss_in_ IUIXMLDocumentInterface *xml_document,
	ss_in_ UIXmlNode node,
	ss_in_ IUIWindowBgCanvasInterface *bg_canvas,
	ss_in_ IUIResourceRenderInterface *res_ren,
	ss_in_ IUIDiskFilesManagerInterface *disk_man,
	ss_out_ scoped_ref_ptr<IUIWindow> &ui_win,
	ss_in_ HWND parent_win_h,
	ss_in_ UINT id,
	ss_in_ HINSTANCE inst
	)
{
	if(xml_document == NULL || node == NULL || bg_canvas == NULL ||
		res_ren == NULL || disk_man == NULL || ui_win != NULL)
		return false;
	if(strcmp(xml_document->NodeName(node), "Window") != 0)
		return false;

	const char *type = NULL, *name = NULL;
	unsigned long transparent_color = 0;
	UIXmlAttribute attribute = xml_document->FirstAttribute(node, "type");
	if(attribute != NULL)
	{
		type = xml_document->AttributeValue(attribute);
	}
	if(type == NULL)
		type = "";

	attribute = xml_document->FirstAttribute(node, "name");
	if(attribute != NULL)
	{
		name = xml_document->AttributeValue(attribute);
	}
	if(name == NULL)
		name = "";

	attribute = xml_document->FirstAttribute(node, "transparent_color");
	if(attribute != NULL)
	{
		if(_stricmp(xml_document->AttributeValue(attribute), "none") == 0)
		{
			transparent_color = 0;
		}else
		{
			if(UIValueParseUtil::ParseColor(xml_document->AttributeValue(attribute), transparent_color) != 1)
			{
				transparent_color = 0;
			}
		}
	}

	HWND parent_win_h_l = parent_win_h;
	UINT id_l = id;
	HINSTANCE inst_l = inst;

	for(IUIBuilderCallbackInterfaceAutoPtrs::iterator iter=build_callbacks_.begin();
		iter!=build_callbacks_.end() && ui_win == NULL; iter++)
	{
		if(!(*iter)->CreateUIWindow(auto_cvt(type).c_tstr(),
			auto_cvt(name).c_tstr(), parent_win_h_l, id_l, inst_l, ui_win))
		{
			parent_win_h_l = parent_win_h;
			id_l = id;
			inst_l = inst;
			SS_ASSERT(ui_win == NULL);
		}
	}

	if(ui_win == NULL)
	{
		if(strlen(type) == 0 || strcmp(type, "window") == 0)
			ui_win.assign_no_ref(new UIWindow);
		else if(strstr(type, "dialog") != NULL)
		{
			tstrings strs = str_split(auto_cvt(type).c_tstr(), _T(':'));
			if(strs.size() == 1)
				ui_win.assign_no_ref(new UIDialog);
			else
				ui_win.assign_no_ref(new UIDialog(strs[1]));

			UIXmlAttribute type_node = xml_document->FirstAttribute(node, "type");
			if(type_node != NULL)
			{
				xml_document->AttributeValue(type_node, "dialog");
			}
		}else if(strcmp(type, "menu_window") == 0)
			ui_win.assign_no_ref(new ui_menu_window);
	}

	if(ui_win == NULL)
		return false;

	if(!ui_win->Init(parent_win_h_l, id_l, inst_l, transparent_color, bg_canvas, res_ren, disk_man, this))
		return false;

	if(!ui_win->ApplyAttributes(this, xml_document, node))
		return false;

	return true;
}

_SSUIL_END
_SSL_END
